using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using System.Xml.Serialization;
using CodeSnippet.Forms;
using CodeSnippet.Helpers;

namespace CodeSnippet.Config
{
    /// <summary>
    /// The plugin's configuration.
    /// </summary>
    public class CodeSnippetConfig
    {
        #region Fields
        /// <summary>
        /// General configuration section.
        /// </summary>
        private GeneralConfig general;
        /// <summary>
        /// Editor configuration section.
        /// </summary>
        private EditorConfig editor;
        /// <summary>
        /// Layout configuration section.
        /// </summary>
        private LayoutConfig layout;
        /// <summary>
        /// Style configuration section.
        /// </summary>
        private StyleConfig style;
        #endregion Fields

        #region Properties
        /// <summary>
        /// Gets or sets the general section of the configuration.
        /// </summary>
        /// <value>The general section of the configuration.</value>
        public GeneralConfig General
        {
            get { return general ?? (general = new GeneralConfig()); }
            set { general = value; }
        }
        /// <summary>
        /// Gets or sets the editor section of the configuration.
        /// </summary>
        /// <value>The editor section of the configuration.</value>
        public EditorConfig Editor
        {
            get { return editor ?? (editor = new EditorConfig()); }
            set { editor = value; }
        }
        /// <summary>
        /// Gets or sets the layout section of the configuration.
        /// </summary>
        /// <value>The layout section of the configuration.</value>
        public LayoutConfig Layout
        {
            get { return layout ?? (layout = new LayoutConfig()); }
            set { layout = value; }
        }
        /// <summary>
        /// Cascading style sheet definitions for different code snippet styles.
        /// </summary>
        public StyleConfig Style
        {
            get { return style ?? (style = new StyleConfig()); }
            set { style = value; }
        }
        #endregion Properties

        #region Volatile Properties
        /// <summary>
        /// Fully qualified name of the configuration file.
        /// </summary>
        [XmlIgnore]
        private static string ConfigFile
        {
            // TODO: Consider refactoring logic used to determine ConfigFile location.
            get
            {
                // Get details about the assembly.
                var assembly = Assembly.GetExecutingAssembly();
                var assemblyFile = assembly.Location;
                if (assemblyFile == null)
                    return string.Empty;
                var assemblyPath = Path.GetDirectoryName(assemblyFile);
                var assemblyCompanyName = Attribute.GetCustomAttribute(assembly, typeof(AssemblyCompanyAttribute)) as AssemblyCompanyAttribute;
                var assemblyProductName = Attribute.GetCustomAttribute(assembly, typeof(AssemblyProductAttribute)) as AssemblyProductAttribute;
                // Prepare the resting folder.
                var configPath = Path.Combine(Path.Combine(Environment.GetEnvironmentVariable("APPDATA") ?? assemblyPath,
                                    assemblyCompanyName == null ? Application.CompanyName : assemblyCompanyName.Company),
                                    assemblyProductName == null ? Application.ProductName : assemblyProductName.Product);
                if (!Directory.Exists(configPath))
                {
                    Directory.CreateDirectory(configPath);
                }
                var configFile = Path.GetFileName(string.Format(CultureInfo.InvariantCulture, "{0}.config", assemblyFile));
                // Return the resulting path.
                return Path.Combine(configPath, configFile);
            }
        }
        /// <summary>
        /// Flag indicating whether or not the form window should be re-positioned.
        /// </summary>
        [XmlIgnore]
        public bool Reposition
        {
            get { return (General.ActiveMode == CodeSnippetViewMode.Compact) ? (Layout.CompactModePlacement.Length != 0) : (Layout.FullModePlacement.Length != 0); }
        }
        /// <summary>
        /// Gets the configuration sections.
        /// </summary>
        /// <value>The configuration sections.</value>
        [XmlIgnore]
        public Collection<object> Sections
        {
            get
            {
                // Create a list of the supported sections so we can expose it to our configuration dialog.
                return new Collection<object>
                           {
                               General,
                               Editor,
                               Style
                           };
            }
        }
        #endregion Volatile Properties

        #region Initialization
        /// <summary>
        /// Construct a <see cref="CodeSnippetConfig"/> instance.
        /// </summary>
        public CodeSnippetConfig()
        {
        }
        /// <summary>
        /// Construct a <see cref="CodeSnippetConfig"/> instance based on an existing one.
        /// </summary>
        /// <param name="copy">The <see cref="CodeSnippetConfig"/> instance from which to construct.</param>
        public CodeSnippetConfig(CodeSnippetConfig copy)
        {
            General = new GeneralConfig(copy.General);
            Editor = new EditorConfig(copy.Editor);
            Layout = new LayoutConfig(copy.Layout);
            Style = new StyleConfig(copy.Style);
        }
        #endregion Initialization

        #region Indexer
        /// <summary>
        /// Gets the <see cref="System.Object"/> with the specified key.
        /// </summary>
        /// <remarks>
        /// <b>DO NOT</b> assume that the section name is fixed. The way it is used in the application
        /// is based on the localized name of the specific section. It is meant for use by the
        /// <see cref="CodeSnippetOptionsForm"/> dialog to automatically match the selected <see cref="TreeNode"/>
        /// to the object to be displayed in its <see cref="PropertyGrid"/> window.
        /// </remarks>
        public object this[string sectionName]
        {
            get
            {
                // Find the section associated to the given key.
                foreach (IConfigSection section in Sections)
                {
                    // The key is based on the section name.
                    if (section.SectionName.Equals(sectionName))
                    {
                        // Return the resulting section.
                        return section;
                    }
                }
                // Return null if we could not find the corresponding section.
                return null;
            }
        }
        #endregion Indexer

        #region Persistence
        /// <summary>
        /// Load the contents of this configuration instance.
        /// </summary>
        /// <returns>The configuration contents.</returns>
        public static CodeSnippetConfig Load()
        {
            return ConfigHelper.LoadConfig(new CodeSnippetConfig(), ConfigFile);
        }
        /// <summary>
        /// Store the contents of this configuration instance.
        /// </summary>
        public void Store()
        {
            ConfigHelper.StoreConfig(this, ConfigFile);
        }
        #endregion Persistence
    }
}